package aop;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.io.*;
import java.util.*;
import java.util.regex.*;

import com.google.common.collect.ImmutableSet;
import com.google.common.reflect.ClassPath;

import aop.*;
import aop.annotation.*;

public class DefaultBeanFactory {
    private Map<String, Bean> beans = new HashMap<String, Bean>();
    //   beanId  映射---->BeanAspects
    protected Map<String, BeanAspects> aops = new HashMap<String, BeanAspects>();

    public DefaultBeanFactory(String[] packages) {
        scanPackages(packages);
    }

    //获取bean
    public Object getBean(String beanId) throws Exception {
        Bean bean = beans.get(beanId);
        if (bean != null) {
            return bean.getInstance(this);
        } else {
            throw new RuntimeException("No such bean: " + beanId);
        }
    }

    //扫描包
    private void scanPackages(String[] packages) {
        for (String pkg : packages) {
            scanBeansInPackage(pkg);
        }
    }

    //扫描包
    private void scanBeansInPackage(String pkg) {
        ClassPath cp;
        try {
            cp = ClassPath.from(getClass().getClassLoader());
        } catch (IOException e) {
            e.printStackTrace();
            //有异常就退出
            return;
        }

        for (ClassPath.ClassInfo ci : cp.getTopLevelClasses(pkg)) {
            //加载类
            Class<?> clazz = ci.load();

            String beanId = clazz.getName();

            // 没有使用Component注解就不加载
            Annotation componentAnnotation = clazz.getAnnotation(Component.class);
            if (componentAnnotation != null) {

                // Add new Bean.
                beans.put(beanId, new Bean(beanId, clazz));

                // Add aspects if it is an aspect bean.
                if (clazz.getAnnotation(Aspect.class) != null) {
                    //切面方法
                    for (Method aspectMethod : clazz.getMethods()) {
                        addBeanAspects(beanId, aspectMethod);
                    }
                }
            }
        }
    }

    //添加切面
    private void addBeanAspects(String aspectBeanId, Method aspectMethod) {

        // 获取切点注解
        PointCut pointCut = aspectMethod.getDeclaredAnnotation(PointCut.class);

        if(pointCut!=null){
            //获取切点方法
            for (Method pointCutMethod : parsePointCutExpr(pointCut.cut())) {
                //获取切点类名称
                String beanId = pointCutMethod.getDeclaringClass().getName();
                //获取切面
                BeanAspects beanAspects = aops.get(beanId);
                if (beanAspects == null) {
                    //切面不存在就创建
                    beanAspects = new BeanAspects();
                    //切点的beanId
                    aops.put(beanId, beanAspects);
                }
                //切面添加切点方法
                beanAspects.addAspectMethod(pointCutMethod, pointCut, aspectBeanId, aspectMethod);
            }
        }

    }

    //解析并获取切点方法
    private List<Method> parsePointCutExpr(String cut) {
        List<Method> result = new ArrayList<Method>();

        //正则表达式
        Pattern r = Pattern.compile("^public \\s*[\\w\\$]+ \\s*(([\\w\\$]+\\.)*([\\w\\$]+))\\.([\\w\\$]+)\\s*\\((.*)\\)$");

        //以分号分割切点
        for (String point : cut.split(";")) {
            Matcher m = r.matcher(point.trim());
            if (!m.find()) {
                System.out.println("Can't parse cut point expression: " + point);
                continue;
            }

            String pointCutClassPath = m.group(1);
            String pointCutMethodName = m.group(4);
            String pointCutMethodArgsList = m.group(5);

            try {
                //class
                Class<?> clazz = Class.forName(pointCutClassPath);
                //方法参数数组
                String[] argTypeListStr = pointCutMethodArgsList.split(",");
                //参数类型的class数组
                Class[] argTypes = new Class[argTypeListStr.length];
                for (int i = 0; i < argTypeListStr.length; i++) {
                    argTypes[i] = getArgClass(argTypeListStr[i].trim());
                }
                result.add(clazz.getMethod(pointCutMethodName, argTypes));
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                continue;
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
                continue;
            }
        }
        return result;
    }

    //返回参数类型的class对象
    private Class getArgClass(String str) throws ClassNotFoundException {
        if (str.equals("int")) {
            return int.class;
        } else if (str.equals("long")) {
            return long.class;
        } else if (str.equals("short")) {
            return short.class;
        } else if (str.equals("float")) {
            return float.class;
        } else if (str.equals("double")) {
            return double.class;
        } else if (str.equals("byte")) {
            return byte.class;
        } else if (str.equals("char")) {
            return char.class;
        } else if (str.equals("boolean")) {
            return boolean.class;
        } else {
            return Class.forName(str);
        }
    }
}
